/*
 * Copyright 2023 Google LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import {ExprCompare} from './expr-compare';
import type {ExpressionDef} from '../types/expression-def';
import type {ExprValue} from '../types/expr-value';
import type {FieldSpace} from '../types/field-space';
import {isGranularResult} from '../types/granular-result';
import {ExprGranularTime} from './expr-granular-time';

export class Apply extends ExprCompare {
  elementType = 'apply';
  constructor(
    readonly left: ExpressionDef,
    readonly right: ExpressionDef
  ) {
    super(left, '=', right);
  }

  getExpression(fs: FieldSpace): ExprValue {
    let right = this.right;
    if (!this.right.granular()) {
      const rhs = this.right.requestExpression(fs);
      if (rhs && isGranularResult(rhs)) {
        // Need to wrap granular computations to get granular behavior
        right = new ExprGranularTime(this.right, rhs.timeframe, false);
      }
    }
    if (right instanceof ExprGranularTime) {
      return right.toRange(fs).apply(fs, this.op, this.left);
    }
    return super.getExpression(fs);
  }
}
